#include <stdlib.h>
#include <string.h>
#include "http_hdrs.h"

/* entity headers */

const char http_hdr_Allow[] = "Allow";
const char http_hdr_Content_Encoding[] = "Content-Encoding";
const char http_hdr_Content_Language[] = "Content-Language";
const char http_hdr_Content_Length[] = "Content-Length";
const char http_hdr_Content_Location[] = "Content-Location";
const char http_hdr_Content_MD5[] = "Content-MD5";
const char http_hdr_Content_Range[] = "Content-Range";
const char http_hdr_Content_Type[] = "Content-Type";
const char http_hdr_Expires[] = "Expires";
const char http_hdr_Last_Modified[] = "Last-Modified";

/* general headers */

const char http_hdr_Cache_Control[] = "Cache-Control";
const char http_hdr_Connection[] = "Connection";
const char http_hdr_Date[] = "Date";
const char http_hdr_Pragma[] = "Pragma";
const char http_hdr_Transfer_Encoding[] = "Transfer-Encoding";
const char http_hdr_Update[] = "Update";
const char http_hdr_Trailer[] = "Trailer";
const char http_hdr_Via[] = "Via";

/* request headers */

const char http_hdr_Accept[] = "Accept";
const char http_hdr_Accept_Charset[] = "Accept-Charset";
const char http_hdr_Accept_Encoding[] = "Accept-Encoding";
const char http_hdr_Accept_Language[] = "Accept-Language";
const char http_hdr_Authorization[] = "Authorization";
const char http_hdr_Expect[] = "Expect";
const char http_hdr_From[] = "From";
const char http_hdr_Host[] = "Host";
const char http_hdr_If_Modified_Since[] = "If-Modified-Since";
const char http_hdr_If_Match[] = "If-Match";
const char http_hdr_If_None_Match[] = "If-None-Match";
const char http_hdr_If_Range[] = "If-Range";
const char http_hdr_If_Unmodified_Since[] = "If-Unmodified-Since";
const char http_hdr_Max_Forwards[] = "Max-Forwards";
const char http_hdr_Proxy_Authorization[] = "Proxy-Authorization";
const char http_hdr_Range[] = "Range";
const char http_hdr_Referrer[] = "Referrer";
const char http_hdr_TE[] = "TE";
const char http_hdr_User_Agent[] = "User-Agent";

/* response headers */

const char http_hdr_Accept_Ranges[] = "Accept-Ranges";
const char http_hdr_Age[] = "Age";
const char http_hdr_ETag[] = "ETag";
const char http_hdr_Location[] = "Location";
const char http_hdr_Retry_After[] = "Retry-After";
const char http_hdr_Server[] = "Server";
const char http_hdr_Vary[] = "Vary";
const char http_hdr_Warning[] = "Warning";
const char http_hdr_WWW_Authenticate[] = "WWW-Authenticate";

/* Other headers */

const char http_hdr_Set_Cookie[] = "Set-Cookie";

/* WebDAV headers */

const char http_hdr_DAV[] = "DAV";
const char http_hdr_Depth[] = "Depth";
const char http_hdr_Destination[] = "Destination";
const char http_hdr_If[] = "If";
const char http_hdr_Lock_Token[] = "Lock-Token";
const char http_hdr_Overwrite[] = "Overwrite";
const char http_hdr_Status_URI[] = "Status-URI";
const char http_hdr_Timeout[] = "Timeout";

const char *http_hdr_known_list[] = 
{
    /* entity headers */
    http_hdr_Allow,
    http_hdr_Content_Encoding,
    http_hdr_Content_Language,
    http_hdr_Content_Length,
    http_hdr_Content_Location,
    http_hdr_Content_MD5,
    http_hdr_Content_Range,
    http_hdr_Content_Type,
    http_hdr_Expires,
    http_hdr_Last_Modified,
    /* general headers */
    http_hdr_Cache_Control,
    http_hdr_Connection,
    http_hdr_Date,
    http_hdr_Pragma,
    http_hdr_Transfer_Encoding,
    http_hdr_Update,
    http_hdr_Trailer,
    http_hdr_Via,
    /* request headers */
    http_hdr_Accept,
    http_hdr_Accept_Charset,
    http_hdr_Accept_Encoding,
    http_hdr_Accept_Language,
    http_hdr_Authorization,
    http_hdr_Expect,
    http_hdr_From,
    http_hdr_Host,
    http_hdr_If_Modified_Since,
    http_hdr_If_Match,
    http_hdr_If_None_Match,
    http_hdr_If_Range,
    http_hdr_If_Unmodified_Since,
    http_hdr_Max_Forwards,
    http_hdr_Proxy_Authorization,
    http_hdr_Range,
    http_hdr_Referrer,
    http_hdr_TE,
    http_hdr_User_Agent,
    /* response headers */
    http_hdr_Accept_Ranges,
    http_hdr_Age,
    http_hdr_ETag,
    http_hdr_Location,
    http_hdr_Retry_After,
    http_hdr_Server,
    http_hdr_Vary,
    http_hdr_Warning,
    http_hdr_WWW_Authenticate,
    NULL
};

/* functions dealing with headers */

const char *
http_hdr_is_known(const char *a_hdr)
{
    int l_pos = 0;
    const char *l_return = NULL;

    if (!a_hdr)
        goto ec;
    while(http_hdr_known_list[l_pos] != NULL)
    {
        if (strcasecmp(a_hdr, http_hdr_known_list[l_pos]) == 0)
        {
            l_return = http_hdr_known_list[l_pos];
            break;
        }
        l_pos++;
    }
ec:
    return l_return;
}

http_hdr_list *
http_hdr_list_new(void)
{
    http_hdr_list *l_return = NULL;

    l_return = (http_hdr_list *)malloc(sizeof(http_hdr_list));
    memset(l_return, 0, sizeof(http_hdr_list));
    return l_return;
}

void
http_hdr_list_destroy(http_hdr_list *a_list)
{
    int i = 0;

    if (a_list == NULL)
        return;
    for(i=0; i < HTTP_HDRS_MAX; i++)
    {
        if (a_list->header[i] && (http_hdr_is_known(a_list->header[i]) == NULL))
            free(a_list->header[i]);
        if (a_list->value[i])
            free(a_list->value[i]);
    }
    free (a_list);
}

int
http_hdr_set_value_no_nts(http_hdr_list *a_list,
                                        const char *a_name_start,
                                        int a_name_len,
                                        const char *a_val_start,
                                        int a_val_len)
{
    int l_return = 0;
    char *l_temp_name = NULL;
    char *l_temp_val = NULL;

    /* note that a zero len value is valid... */
    if ((a_list == NULL) || (a_name_start == NULL) ||
        (a_val_start == NULL) || (a_name_len == 0))
        goto ec;
    l_temp_name = (char *)malloc(a_name_len + 1);
    memset(l_temp_name, 0, a_name_len + 1);
    memcpy(l_temp_name, a_name_start, a_name_len);
    l_temp_val = (char *)malloc(a_val_len + 1);
    memset(l_temp_val, 0, a_val_len + 1);
    memcpy(l_temp_val, a_val_start, a_val_len);
    /* set the value */
    l_return = http_hdr_set_value(a_list,l_temp_name,l_temp_val);
    free(l_temp_name);
    free(l_temp_val);
ec:
    return l_return;
}

int
http_hdr_set_value(http_hdr_list *a_list,
                                const char *a_name,
                                const char *a_val)
{
    int i = 0;
    char *l_temp_value = NULL;
    int l_return = 0;

    if ((a_list == NULL) || (a_name == NULL) || (a_val == NULL))
        goto ec;
    l_temp_value = http_hdr_get_value(a_list, a_name);
    if (l_temp_value == NULL)
    {
        for (i=0; i < HTTP_HDRS_MAX; i++)
        {
            if (a_list->header[i] == NULL)
            {
                /* I promise not to mess with this value. */
                l_temp_value = (char *)http_hdr_is_known(a_name);
                if (l_temp_value)
                    a_list->header[i] = l_temp_value;
                else
                    a_list->header[i] = strdup(a_name);
                
                a_list->value[i] = strdup(a_val);
                l_return = 1;
                break;
            }
        }
    }
    else
    {
        for(i = 0; i < HTTP_HDRS_MAX; i++)
        {
            if (a_list->value[i] == l_temp_value)
            {
                free(a_list->value[i]);
                a_list->value[i] = strdup(a_val);
                l_return = 1;
                break;
            }
        }
    }
ec:
    return l_return;
}

char *
http_hdr_get_value(http_hdr_list *a_list,
                                const char *a_name)
{
    int i = 0;
    char *l_return = NULL;

    if (a_name == NULL)
        goto ec;
    
    for (i=0; i < HTTP_HDRS_MAX; i++)
    {
        if (a_list->header[i] && (strcasecmp(a_list->header[i], a_name) == 0))
        {
            if (a_list->value[i] == NULL)
                goto ec;
            
            l_return = a_list->value[i];
            break;
        }
    }
ec:
    return l_return;
}

int
http_hdr_get_headers(http_hdr_list *a_list, char ***a_names,int *a_num_names)
{
    int i = 0;
    int l_num_names = 0;
    char **l_names;

    if (a_num_names == NULL)
        return -1;
    if (a_names == NULL)
        return -1;

    /* set our return values */
    *a_names = NULL;
    *a_num_names = 0;

    /* make a pass to find out how many headers we have. */
    for (i=0; i < HTTP_HDRS_MAX; i++)
    {
        if (a_list->header[i])
        l_num_names++;
    }

    /* return if there are no headers */
    if (l_num_names == 0)
        return 0;

    /* now that we know how many headers we have allocate the number of
    slots in the return */
    l_names = malloc(sizeof(char *) * l_num_names);
    if (l_names == NULL)
        return -1;

    /* zero the list so that we can clean up later if we have to */
    memset(l_names, 0, l_num_names);

    /* copy the headers */
    for (i=0; i < HTTP_HDRS_MAX; i++)
    {
        if (a_list->header[i])
        {
            l_names[i] = strdup(a_list->header[i]);
            if (l_names[i] == NULL)
                goto ec;
        }
    }

    *a_names = l_names;
    *a_num_names = l_num_names;
    return 0;

    /* something bad happened.  Try to free up as much as possible. */
ec:
    if (l_names)
    {
        for (i=0; i < l_num_names; i++)
        {
            if (l_names[i])
            {
                free(l_names[i]);
                l_names[i] = NULL;
            }
        }
        free(l_names);
        *a_names = 0;
    }
    *a_num_names = 0;
    return -1;
}

int
http_hdr_clear_value(http_hdr_list *a_list,const char *a_name)
{
    int i = 0;
    int l_return = 0;

    if ((a_list == NULL) || (a_name == NULL))
        goto ec;
    for (i=0; i < HTTP_HDRS_MAX; i++)
    {
        if (a_name && a_list->header[i] && (strcasecmp(a_list->header[i], a_name) == 0))
        {
            if (http_hdr_is_known(a_name) == NULL)
                free(a_list->header[i]);
            a_list->header[i] = NULL;
            free(a_list->value[i]);
            a_list->value[i] = NULL;
        }
    }
ec:
    return l_return;
}

